package com.young.ums.service.impl;

import com.young.common.exception.BaseException;
import com.young.common.util.*;
import com.young.common.core.dal.service.IDataAccessService;
import com.young.interfaces.log.annotation.Log;
import com.young.ums.core.ETopic;
import com.young.ums.core.PublishSubscribeCenter;
import com.young.ums.model.Dept;
import com.young.ums.model.enums.EDeptTypeEnum;
import com.young.ums.service.IDataAuthService;
import com.young.ums.service.IDeptService;
import com.young.ums.cache.IUmsCacheService;
import com.young.ums.util.UmsConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
* 组织机构信息服务实现
* Created by imrookie on 2018-9-24.
*/
@Service("deptService")
public class DeptServiceImpl implements IDeptService {

    @Resource(name = "dataAccessService")
    IDataAccessService dataAccessService;//数据层服务

    @Resource
    PublishSubscribeCenter publishSubscribeCenter;

    @Resource(name = "dataAuthService")
    IDataAuthService dataAuthService;

    @Autowired
    IUmsCacheService cacheService;

    private static final Logger logger = LoggerFactory.getLogger(DeptServiceImpl.class);

    private static final String MAPPER = "com.young.ums.mapper.dept";//mapper的namespace

    //获取数据列表
    @Log("查询组织机构")
    @Override
    public List<Dept> getList(Dept obj) {
        //权限参数处理
        dataAuthService.handleAuthorityParameter(obj);
        return dataAccessService.getList(MAPPER + ".getList", obj);
    }

    /**
     * 单位树查询(只返回树中使用的字段,解决数据量大的情况下性能问题)
     *
     * @param obj
     * @return
     */
    @Log("单位树查询")
    @Override
    public List<Dept> getListAsTree(Dept obj) {
        //权限参数处理
        dataAuthService.handleAuthorityParameter(obj);

        //数据范围id
        String rangeId = UmsConstants.DEFAULT.ROOT_DEPT;
        if (obj.enableAuth()) {
            //根据管理范围拼接rangeId
            rangeId = obj.getManageRangeList().stream().map(Dept::getId).sorted().collect(Collectors.joining("@"));
        }

        //查询缓存数据
        List<Dept> cacheList = cacheService.getDeptTreeList(obj.getType(), rangeId);
        if (CollectionUtil.isNotEmpty(cacheList)) {
            logger.info("[单位树查询] 返回从缓存中查到的树,类型={},范围={},缓存数据条数={}", obj.getType(), rangeId, cacheList.size());
            return cacheList;
        }

        //从数据库查
        List<Dept> listDb = dataAccessService.getList(MAPPER + ".getListAsTree", obj);
        if (CollectionUtil.isNotEmpty(listDb)) {
            cacheService.setDeptTreeList(obj.getType(), rangeId, listDb);
            logger.info("[单位树查询] 缓存当前单位树,类型={},范围={},缓存数据条数={}", obj.getType(), rangeId, listDb.size());
        }
        return listDb;
    }

    //获取数据列表(分页)
    @Log("分页查询组织机构")
    @Override
    public List<Dept> getPage(Dept obj, Page page) {
        //权限参数处理
        dataAuthService.handleAuthorityParameter(obj);
        return dataAccessService.getList(MAPPER + ".getList", obj, page);
    }

    //查询单条
    @Log("查询组织机构")
    @Override
    public Dept get(String id) {
        return dataAccessService.getObject(MAPPER + ".get", id);
    }

    /**
     * 根据单位id查询上级单位
     * 因为只有单位才能创建子单位(部门下不允许建单位),所以根据parentId查询出来的必定是单位
     *
     * @param companyId 单位id
     * @return
     */
    @Log("查询上级单位")
    @Override
    public Dept getParentCompany(String companyId) {
        Dept company = this.get(companyId);
        if (company == null) {
            return null;
        }
        if (EDeptTypeEnum.COMPANY.notEq(company.getType())) {
            logger.warn("[查询指定单位的上级单位] 入参[{}]的类型[{}]不属于单位类型COMPANY", companyId, company.getType());
            return null;
        }
        if (StringUtils.isBlank(company.getParentId())) {
            return null;
        }
        return this.get(company.getParentId());
    }

    /**
     * 根据单位id查询所有的上级单位,一条链查到根
     *
     * @param companyId 单位id
     * @return
     */
    @Log("根据单位id查询所有的上级单位")
    @Override
    public List<Dept> getAllParentCompany(String companyId) {
        Dept company = this.get(companyId);
        if (company == null) {
            return null;
        }
        if (EDeptTypeEnum.COMPANY.notEq(company.getType())) {
            logger.warn("[查询指定单位的所有上级单位] 入参[{}]的类型[{}]不属于单位类型COMPANY", companyId, company.getType());
            return null;
        }
        if (StringUtils.isBlank(company.getParentId())) {
            return null;
        }
        return dataAccessService.getList(MAPPER + ".getAllParentCompany", company);
    }

    //统计数量
    @Log("统计组织机构")
    @Override
    public int count(Dept obj) {
        return dataAccessService.getObject(MAPPER + ".count", obj);
    }

    //修改
    @Log("修改组织机构")
    @Override
    public int update(Dept obj) {
        //查询修改前的老机构信息,判断上级机构是否发生改变,若发生该表,则设置新的id和name全路径,设置机构级别
        Dept oldDept = this.get(obj.getId());
        if (oldDept == null){
            logger.error("[更新机构信息] 机构查询无效,机构id={}", obj.getId());
            throw new BaseException("机构信息无效");
        }

        if (UmsConstants.DEFAULT.ROOT_DEPT.equals(obj.getId())){//根节点更新
            obj.setParentId(null);
        }

        //上级发生改变或名字发生改变,则刷新id和name全路径和机构级别
        boolean refresh = false;//判断是否需要刷新子孙节点的全路径信息
        if ((!UmsConstants.DEFAULT.ROOT_DEPT.equals(obj.getId()) && !StringUtils.equals(obj.getParentId(), oldDept.getParentId())) || !StringUtils.equals(obj.getName(), oldDept.getName())){
            if (UmsConstants.DEFAULT.ROOT_DEPT.equals(obj.getId())){//根节点
                obj.setWholeId("/" + obj.getId());
                obj.setWholeName("/" + obj.getName());
                obj.setDeptLevel(1);
            }else{
                //查询父节点,然后设置id和name全路径,设置机构级别
                Dept parentDept = this.get(obj.getParentId());
                if (parentDept == null){
                    logger.error("[更新机构信息] 上级机构查询无效,机构id={},上级机构id={}", obj.getId(), obj.getParentId());
                    throw new BaseException("上级机构信息无效");
                }
                obj.setWholeId(parentDept.getWholeId() + "/" + obj.getId());
                obj.setWholeName(parentDept.getWholeName() + "/" + obj.getName());
                obj.setDeptLevel(parentDept.getDeptLevel() + 1);
            }
            refresh = true;//路径信息发生变化,子孙节点需要更新
        }

        //上级机构发生变化或者机构类型发生变化,更新机构类型信息
        if ((!UmsConstants.DEFAULT.ROOT_DEPT.equals(obj.getId()) && !StringUtils.equals(obj.getParentId(), oldDept.getParentId())) || (obj.getType() != null && !oldDept.getType().equals(obj.getType())) ){
            if (UmsConstants.DEFAULT.ROOT_DEPT.equals(obj.getId())){
                throw new BaseException("ROOT节点类型不允许更改");
            }
            //父节点信息
            Dept parentDept = this.get(obj.getParentId());
            //若节点类型为单位,但父节点不是单位类型,报错:只有单位节点下才可以创建单位子节点
            if (EDeptTypeEnum.COMPANY.eq(obj.getType()) && EDeptTypeEnum.COMPANY.notEq(parentDept.getType())){
                logger.info("[机构更新] 只有单位节点下才可以创建单位子节点,节点id={},待更新类型={},父节点id={},父节点类型={}", obj.getId(), obj.getType(), parentDept.getId(), parentDept.getType());
                throw new BaseException("非法的机构类型");
            }
            //机构类型为单位时,所属单位等于他自身,否则等于父节点的所属单位
            if (EDeptTypeEnum.COMPANY.eq(obj.getType())){
                obj.setCompanyId(obj.getId());
            } else {
                obj.setCompanyId(parentDept.getCompanyId());
            }
            refresh = true;//机构类型发生变化,子孙节点需要更新
        }

        //更新
        obj.setUpdateTime(new Date());//设置更新时间
        dataAccessService.update(MAPPER + ".update", obj);

        //全路径发生改变,所有子孙节点的全路径和级别跟着更新
        if (refresh){
            refreshAllChildRankInfo(obj, 1);
        }

        //发生修改时主动删除树缓存
        cacheService.removeDeptTreeList();
        return 1;
    }

    /**
     * 刷新所有子孙节点的层级信息(全路径和级别)
     * @param parentDept
     */
    private void refreshAllChildRankInfo(Dept parentDept, int count) {
        if (count > 20){
            logger.error("[刷新组织子孙节点层级信息] 子孙节点深度过大,请检查是否存在节点之间循环引用");
            throw new BaseException("节点可能存在循环引用");
        }
        //儿子节点
        List<Dept> childList = this.getChildList(parentDept.getId());
        if (childList != null && childList.size() > 0){
            for (Dept child : childList){
                //查询父节点,然后设置id和name全路径,设置机构级别
                if (parentDept == null){
                    logger.error("[更新机构信息] 上级机构无效");
                    throw new BaseException("上级机构信息无效");
                }
                child.setWholeId(parentDept.getWholeId() + "/" + child.getId());
                child.setWholeName(parentDept.getWholeName() + "/" + child.getName());
                child.setDeptLevel(parentDept.getDeptLevel() + 1);
                child.setUpdateTime(new Date());//设置更新时间
                //机构类型为单位时,所属单位等于他自身,否则等于父节点的所属单位
                if (EDeptTypeEnum.COMPANY.eq(child.getType())){
                    child.setCompanyId(child.getId());
                } else {
                    child.setCompanyId(parentDept.getCompanyId());
                }
                //更新节点信息
                dataAccessService.update(MAPPER + ".update", child);
                //递归更新子节点信息
                refreshAllChildRankInfo(child, count + 1);
            }
        }

        //发生修改时主动删除树缓存
        cacheService.removeDeptTreeList();
    }

    //批量删除
    @Log("批量删除组织机构")
    @Override
    public int delete(String[] idArr) {
        int i = 0;
        for(String id : idArr){
            i += this.delete(id);
        }
        return i;
    }

    //删除单条
    @Log("删除组织机构")
    @Override
    public int delete(String id) {
        if (StringUtils.isBlank(id)){
            throw new BaseException("组织删除失败:编号无效");
        }
        //判断是否存在子节点
        Dept dept = new Dept();
        dept.setParentId(id);
        int childNum = this.count(dept);
        if (childNum > 0){
            logger.error("[删除组织] 当前组织[{}]存在子节点(数量:{}),不允许删除", id, childNum);
            throw new BaseException("组织删除失败:存在子节点");
        }
        int num = dataAccessService.delete(MAPPER + ".delete", id);
        //向发布订阅中心发布删除消息
        publishSubscribeCenter.publish(ETopic.DEPT_DEL, id);
        //发生修改时主动删除树缓存
        cacheService.removeDeptTreeList();
        return num;
    }

    //插入
    @Log("插入组织机构")
    @Override
    public int insert(Dept obj) {
        obj.setId(CommonUtil.getUUID());
        if (StringUtils.isBlank(obj.getCode())){
            obj.setCode(obj.getId());
        }
        if(StringUtils.isBlank(obj.getParentId())){//默认上级部门为根节点
            obj.setParentId(UmsConstants.DEFAULT.ROOT_DEPT);
            obj.setType(EDeptTypeEnum.COMPANY.type);
        }

        //查询父节点,然后设置id和name全路径,设置机构级别
        Dept parentDept = this.get(obj.getParentId());
        if (parentDept == null){
            logger.error("[插入机构信息] 上级机构查询无效,上级机构id={}", obj.getParentId());
            throw new BaseException("上级机构信息无效");
        }
        obj.setWholeId(parentDept.getWholeId() + "/" + obj.getId());
        obj.setWholeName(parentDept.getWholeName() + "/" + obj.getName());
        obj.setDeptLevel(parentDept.getDeptLevel() + 1);

        //机构类型为单位时,所属单位等于他自身,否则等于父节点的所属单位
        if (EDeptTypeEnum.COMPANY.eq(obj.getType())){
            obj.setCompanyId(obj.getId());
        } else {
            obj.setCompanyId(parentDept.getCompanyId());
        }

        //设置创建时间和更新时间
        obj.setCreateTime(new Date());
        obj.setUpdateTime(new Date());

        int num = dataAccessService.insert(MAPPER + ".insert", obj);
        //发生修改时主动删除树缓存
        cacheService.removeDeptTreeList();
        return num;
    }

    //批量插入
    @Log("批量插入组织机构")
    @Override
    public int batchInsert(List<Dept> list) {
        int i = 0;
        for(Dept item : list){
            i += this.insert(item);
        }
        return i;
    }

    /**
     * 获取儿子节点(不含孙子)
     *
     * @param deptId 机构id
     * @return
     */
    @Log("获取直接儿子节点")
    @Override
    public List<Dept> getChildList(String deptId) {
        return dataAccessService.getList(MAPPER + ".getChildList", deptId);
    }

    /**
     * 根据机构id数组查询机构列表
     *
     * @param deptIds 机构id数组
     * @return
     */
    @Log("根据机构id数组查询机构列表")
    @Override
    public List<Dept> getListByIds(String[] deptIds) {
        return dataAccessService.getList(MAPPER + ".getListByIds", deptIds);
    }

}